import type {
  MaybeElementRef,
  UseFullscreenOptions,
} from '@vueuse/core'
import {
  useFullscreen as _useFullscreen,
} from '@vueuse/core'
import type {
  MaybeRefOrGetter,
  WatchStopHandle,
} from 'vue'

export interface NumberRunOptions {
  /**
   * 初始值
   * @default 1000
   */
  initNum: number
  /**
   * 目标值
   * @default 1000
   */
  targetNum:
    | number
    | MaybeRefOrGetter<number>
    | ComputedRef<number>
  /**
   * 循环次数
   * @default 50
   */
  frequency: number
  /**
   * 保留小数位数
   * @default 4
   */
  hold: number
  /**
   * 监听
   * 当targetNum值为引用类型，可开启自动监听,可舍去主动更新
   * @description 通过toValue、watch方法
   */
  watch: boolean
}
function randomNum() {
  return Math.floor(Math.random() * 9) + 1
}
/**
 * 数字运动
 */
export function useNumberRun(
  options?: Partial<NumberRunOptions>,
) {
  // #region
  const initNum = options?.initNum ?? 1000
  let targetNum = toValue(options?.targetNum) ?? 1000
  let frequency = options?.frequency ?? 50
  let hold = options?.hold ?? 4
  // #endregion
  const [value, setValue] = useState(String(initNum))
  let global: number
  /** 每次渲染显示的数字 */
  let numText = ''
  /** 记录当前循环次数 */
  let i = 0
  /** 监听控制器 */
  let watchStopHandle: WatchStopHandle | null
  /** 数字长度 */
  let l = targetNum.toFixed(hold).length

  numberGlobal()
  options?.watch && openWatch()

  function openWatch() {
    watchStopHandle?.()
    watchStopHandle = watch(
      () => toValue(options?.targetNum),
      (v?: number) => update(v),
    )
  }
  function numberGlobal() {
    let m = ''

    for (const _ of range(l)) {
      m += randomNum()
    }
    numText = m // 速度的计算可以为小数
    if (i >= frequency) {
      numText = targetNum.toFixed(hold)
      cancelAnimationFrame(global)
    }
    else {
      global = requestAnimationFrame(numberGlobal)
      i++
    }
    setValue(numText)
  }
  function update(
    v?: number,
    opts?: Partial<
      Omit<NumberRunOptions, 'initNum' | 'targetNum'>
    >,
  ) {
    if (typeof v === 'number') {
      targetNum = v
      // 数值为零添加动画
      if (!v) {
        l = 10
      }
      else {
        l = targetNum.toFixed(hold).length
      }
    }
    else {
      targetNum
        = toValue(options?.targetNum)
        ?? options?.initNum
        ?? initNum
      l = targetNum.toFixed(hold).length
    }
    i = 0
    opts?.frequency && (frequency = opts?.frequency)
    opts?.hold && (hold = opts?.hold)
    numberGlobal()
  }
  return [value, update] as const
}

type ScrollTarget =
  | string
  | Element
  | null
  | MaybeRefOrGetter<string | Element | null>
  | ComputedRef<string | Element | null>
interface ScrollOptions {
  /**
   * 速度
   */
  speed: number
  /**
   * 方向
   * @default vertical
   */
  direction: 'vertical' | 'horizontal'
}
/**
 * 滚动列表
 */
export function useScroll(
  target: ScrollTarget,
  options?: Partial<ScrollOptions>,
): [globalThis.Ref<number>, () => void, () => void] {
  let i = 0
  let global: number
  const speed = options?.speed ?? 0.5
  const direction = options?.direction ?? 'vertical'
  // 记录滚动距离
  const [scrollDistance, setScrollDistance] = useState(0)
  // 记录滚动列表高度
  const [scrollSize, setScrollSize] = useNativeState(0)
  // 记录滚动视图高度
  const [scrollViewSize, setScrollViewSize]
    = useNativeState(0)
  // 记录目标节点
  const [targetNode, setTargetNode]
    = useNativeState<null | HTMLElement>(null)
  /**
   * 各项数据初始化
   * 根据方向生成初始化函数
   */
  const init = (() => {
    if (direction === 'vertical') {
      return (node: ScrollTarget = toValue(target)) => {
        end()
        if (!node)
          return
        if (typeof node === 'string') {
          setTargetNode(document.querySelector(node))
        }
        else {
          setTargetNode(toValue(node) as HTMLElement)
        }
        setScrollViewSize(
          targetNode.value?.offsetHeight ?? 0,
        )
        setScrollSize(targetNode.value?.scrollHeight ?? 0)
      }
    }
    else {
      return (node: ScrollTarget = toValue(target)) => {
        end()
        if (!node)
          return
        if (typeof node === 'string') {
          setTargetNode(document.querySelector(node))
        }
        else {
          setTargetNode(toValue(node) as HTMLElement)
        }
        setScrollViewSize(
          targetNode.value?.offsetWidth ?? 0,
        )
        setScrollSize(targetNode.value?.scrollWidth ?? 0)
      }
    }
  })()
  const scrollTo = (() => {
    if (direction === 'vertical') {
      return (top: number) =>
        targetNode.value?.scrollTo?.({ top })
    }
    else {
      return (left: number) =>
        targetNode.value?.scrollTo?.({ left })
    }
  })()

  async function handle() {
    if (
      scrollDistance.value
      >= scrollSize.value - scrollViewSize.value
    ) {
      cancelAnimationFrame(global)
      i = 0
      setScrollDistance(i)
      scrollTo(i)
      await nextTick()
      handle()
    }
    else {
      global = requestAnimationFrame(handle)
      i += speed
      setScrollDistance(i)
      scrollTo(i)
    }
  }

  function start() {
    init()
    // watchDebounced(() => toValue(target), init, {
    //   debounce: 300
    // });
    handle()
  }
  function end() {
    cancelAnimationFrame(global)
    i = 0
  }

  return [scrollDistance, start, end]
}

/**
 * 判断浏览器是否处于全屏状态
 */
function isBrowserFullscreen() {
  return !!(
    document.fullscreenElement
    || (document as any).webkitFullscreenElement
    || (document as any).mozFullScreenElement
    || (document as any).msFullscreenElement
  )
}
/**
 * 检测页面是否可见
 */
function isPageVisible() {
  return !document.hidden
}
/**
 * 系统全屏
 * @description 在vueuse useFullscreen基础上扩展
 */
export function useFullscreen(
  target?: MaybeElementRef,
  options?: UseFullscreenOptions,
) {
  const { isFullscreen, toggle, enter, exit }
    = _useFullscreen(target, options)

  const handleKeydown = (e: KeyboardEvent) => {
    if (e.key !== 'F11')
      return
    e.stopPropagation()
    e.preventDefault()
    toggle()
  }
  // const handleVisibilitychange = () => {
  //   if (!isPageVisible()) return;
  //   isBrowserFullscreen() ? enter() : exit();
  // };
  window.addEventListener('keydown', handleKeydown)
  // 监听页面可见性变化事件
  // window.addEventListener(
  //   'visibilitychange',
  //   handleVisibilitychange
  // );

  onBeforeUnmount(() => {
    window.removeEventListener('keydown', handleKeydown)
    // window.addEventListener(
    //   'visibilitychange',
    //   handleVisibilitychange
    // );
  })
  return {
    isFullscreen,
    toggle,
    enter,
    exit,
  }
}
